import opus from "./opus.js";
import {buildOpusEventInd, buildOpusEventRsp, OPUS_EVENT} from "./opus_event.js";
import {WeilaRingBuffer} from "../utils/weila_ringbuffer";

let opusEncoder = undefined;
let pcmBuffer = undefined;
let codedBuffer = undefined;
let opusModule = undefined;
let sourceSampleRate = 0;
let frameCount = 0;
let frameRingBuffer = undefined;
let pcmArray = undefined;

self.onmessage = (event) => {
    let evt = event.data.event & 0xFF00;
    let data = event.data.data;

    switch (evt) {
        case OPUS_EVENT.OPUS_INIT_WORKER_EVT: {
            let module = {};
            module['wasmBinary'] = data.wasmData;
            opus(module).then(value => {
                let errCode = 0, errMsg = "ok";
                console.log("success load opus", value);
                opusModule = value;

                codedBuffer = opusModule._malloc(256);
                if (!codedBuffer) {
                    errCode = -1;
                    errMsg = "allocate codedBuffer fail";
                }

                if (errCode === 0) {
                    pcmBuffer = opusModule._malloc(1024 * Int16Array.BYTES_PER_ELEMENT);
                    if (!pcmBuffer) {
                        opusModule._free(codedBuffer);
                        errCode = -1;
                        errMsg = "allocate pcmBuffer fail";
                    }
                }

                let rsp = buildOpusEventRsp(evt, {errCode: errCode, errMsg: errMsg});
                self.postMessage(rsp);
            }).catch(reason => {
                console.log("fail to load opus", reason);
                let rsp = buildOpusEventRsp(evt, {errCode: -1, errMsg: reason});
                self.postMessage(rsp);
            })
        }
        break;

        case OPUS_EVENT.OPUS_START_ENCODE_EVT: {
            let errCode = 0, errMsg = "ok"
            sourceSampleRate = data.sampleRate;
            frameCount = Math.ceil(sourceSampleRate / 50);
            console.log("OPUS_START_ENCODE_EVT:", data.sampleRate, frameCount);
            opusEncoder = opusModule._Opus_initEncoder(data.sampleRate, 1, data.bitRate);
            if (!opusEncoder) {
                errCode = -1;
                errMsg = "init encoder fail"
            }else {
                frameRingBuffer = new WeilaRingBuffer(sourceSampleRate * 60, false);
                pcmArray = new Int16Array(frameCount);
            }

            const rsp = buildOpusEventRsp(OPUS_EVENT.OPUS_START_ENCODE_EVT, {
                errCode: errCode, errMsg: errMsg});
            self.postMessage(rsp);
        }
        break;

        case OPUS_EVENT.OPUS_ENCODE_DATA_EVT: {
            if (opusEncoder) {
                frameRingBuffer.push(data.data);
                encodePcm();
            }
        }
        break;

        case OPUS_EVENT.OPUS_STOP_ENCODE_EVT: {
            opusModule._Opus_destroyEncoder(opusEncoder);
            opusEncoder = undefined;
            if (frameRingBuffer) {
                frameRingBuffer.clear();
                frameRingBuffer = undefined;
            }
            pcmArray = null;
            const rsp = buildOpusEventRsp(OPUS_EVENT.OPUS_STOP_ENCODE_EVT, {errCode: 0, errMsg: "ok"});
            self.postMessage(rsp);
        }
        break;
    }
}

function encodePcm() {
    while(frameRingBuffer.frameAvailable >= frameCount) {
        frameRingBuffer.pull(pcmArray);
        const bytesBuffer = new Uint8Array(pcmArray.buffer);
        opusModule.writeArrayToMemory(bytesBuffer, pcmBuffer);
        const codedLen = opusModule._Opus_encode(opusEncoder, pcmBuffer, frameCount, codedBuffer, 256);
        if (codedLen > 0) {
            let codedArr = new Uint8Array(opusModule.HEAP8.buffer, codedBuffer, codedLen);
            try {
                const ind = buildOpusEventInd(OPUS_EVENT.OPUS_ENCODE_DATA_EVT, {type: "buffer", data: codedArr});
                self.postMessage(ind);
                codedArr = null;
            } catch (e) {
                const dataList = [];
                codedArr.forEach(value => {
                    dataList.push(value);
                })

                codedArr = null;
                const ind = buildOpusEventInd(OPUS_EVENT.OPUS_ENCODE_DATA_EVT, {type: "array", data: dataList});
                self.postMessage(ind);
            }
        }
    }
}